#include <func.h>

int sendFd(int pipeFd,int fd)
{
    struct msghdr hdr;
    memset(&hdr,0,sizeof(hdr));

    //用户态必须写一些信息
    struct iovec iov[1];
    char buf[] ="hello";
    iov[0].iov_base=buf;
    iov[0].iov_len=5;
    hdr.msg_iov=iov;
    //表示消息头 hdr 中描述消息的数据部分的 iovec 结构体数组中只包含了一个元素
    hdr.msg_iovlen=1;

    //其中 sizeof(int) 是要传递的文件描述符的大小，
    //CMSG_LEN() 是一个宏，用于计算包含指定长度的控制信息的总长度。
    size_t cmsglen=CMSG_LEN(sizeof(int));
    struct cmsghdr *pcmsg=(struct cmsghdr *)calloc(1,cmsglen);
    pcmsg->cmsg_len=cmsglen;
    pcmsg->cmsg_level=SOL_SOCKET;
    pcmsg->cmsg_type=SCM_RIGHTS;
    *(int *)CMSG_DATA(pcmsg)=fd;//要传递那个描述符的控制信息

    //数据部分是文件描述符
    hdr.msg_control=pcmsg;
    hdr.msg_controllen=cmsglen;

    int ret=sendmsg(pipeFd,&hdr,0);
    ERROR_CHECK(ret,-1,"sendmsg");
    return 0;

}

//fd是传出参数
int recvFd(int pipeFd,int *fd)
{
    struct msghdr hdr;
    memset(&hdr,0,sizeof(hdr));

    //用户态必须写一些信息
    struct iovec iov[1];
    char buf[6] = {0};
    iov[0].iov_base = buf;
    iov[0].iov_len = 5;
    hdr.msg_iov = iov;
    hdr.msg_iovlen = 1;

    size_t cmsglen=CMSG_LEN(sizeof(int));
    struct cmsghdr *pcmsg=(struct cmsghdr *)calloc(1,cmsglen);
    pcmsg->cmsg_len=cmsglen;//第一个成员是结构体本身的长度
    pcmsg->cmsg_level = SOL_SOCKET;
    pcmsg->cmsg_type = SCM_RIGHTS;

    //数据部分是文件描述符
    hdr.msg_control = pcmsg;
    hdr.msg_controllen = cmsglen;

    int ret=recvmsg(pipeFd,&hdr,0);
    ERROR_CHECK(ret,-1,"recvmsg");
    *fd=*(int*)CMSG_DATA(pcmsg);//拿到了对应内核控制信息的位置
    return 0;
}

int main()
{
    int fds[2];
    //初始化tcp类型管道（全双工）
    socketpair(AF_LOCAL,SOCK_STREAM,0,fds);
    if(!fork())
    {
        close(fds[1]);//关闭写端
        int fd;
        recvFd(fds[0],&fd);
        printf("i am child %d\n",fd);
        //读文件
        char buf[1000]={0};
        read(fd,buf,sizeof(buf));
        printf("buf=%s\n",buf);

    }else{
        close(fds[0]);//关闭读端
        int fd=open("Readme",O_RDONLY);
        printf("i am parent fd=%d\n",fd);
        sendFd(fds[1],fd);
        wait(NULL);
    }
}